﻿using System;
using System.Runtime.Serialization;
using System.Text;
using lucere.analysis.query.parse;

namespace lucere.exception
{
    [Serializable]
    public class ParseException : Exception 
    {
        /// <summary> This method has the standard behavior when this object has been
        /// created using the standard constructors.  Otherwise, it uses
        /// "currentToken" and "expectedTokenSequences" to generate a parse
        /// error message and returns it.  If this object has been created
        /// due to a parse error, and you do not catch it (it gets thrown
        /// from the parser), then this method is called during the printing
        /// of the final stack trace, and hence the correct error message
        /// gets displayed.
        /// </summary>
        public override System.String Message
        {
            get
            {
                if (!specialConstructor)
                {
                    return base.Message;
                }

                StringBuilder expected = new StringBuilder();
                int maxSize = 0;
                for (int i = 0; i < expectedTokenSequences.Length; i++)
                {
                    if (maxSize < expectedTokenSequences[i].Length)
                    {
                        maxSize = expectedTokenSequences[i].Length;
                    }
                    
                    for (int j = 0; j < expectedTokenSequences[i].Length; j++)
                    {
                        expected.Append(tokenImage[expectedTokenSequences[i][j]]).Append(' ');
                    }
                    
                    if (expectedTokenSequences[i][expectedTokenSequences[i].Length - 1] != 0)
                    {
                        expected.Append("...");
                    }
                    
                    expected.Append(eol).Append("    ");
                }
                
                string retval = "Encountered \"";
                IToken tok = currentToken.next;
                
                for (int i = 0; i < maxSize; i++)
                {
                    if (i != 0)
                        retval += " ";
                    if (tok.kind == 0)
                    {
                        retval += tokenImage[0];
                        break;
                    }
                    retval += (" " + tokenImage[tok.kind]);
                    retval += " \"";
                    retval += Add_escapes(tok.image);
                    retval += " \"";
                    tok = tok.next;
                }
                
                retval += ("\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn);
                retval += ("." + eol);
                
                if (expectedTokenSequences.Length == 1)
                {
                    retval += ("Was expecting:" + eol + "    ");
                }
                else
                {
                    retval += ("Was expecting one of:" + eol + "    ");
                }
                retval += expected.ToString();
                return retval;
            }

        }

        public ParseException()
        {
            specialConstructor = false;
		}

        public ParseException(IToken currentTokenVal, int[][] expectedTokenSequencesVal, string[] tokenImageVal)
            : base("")
        {
            specialConstructor = true;
            currentToken = currentTokenVal;
            expectedTokenSequences = expectedTokenSequencesVal;
            tokenImage = tokenImageVal;
        }

		public ParseException(string message) : base(message)
		{
            specialConstructor = false;
		}

		public ParseException(string message, Exception innerException) : base(message, innerException)
		{
            specialConstructor = false;
		}

        protected ParseException(SerializationInfo info, StreamingContext context)
            : base(info, context)
		{
            specialConstructor = false;
		}

        /// <summary> This variable determines which constructor was used to create
        /// this object and thereby affects the semantics of the
        /// "getMessage" method (see below).
        /// </summary>
        protected internal bool specialConstructor;

        /// <summary> This is the last token that has been consumed successfully.  If
        /// this object has been created due to a parse error, the token
        /// followng this token will (therefore) be the first error token.
        /// </summary>
        public IToken currentToken;

        /// <summary> Each entry in this array is an array of integers.  Each array
        /// of integers represents a sequence of tokens (by their ordinal
        /// values) that is expected at this point of the parse.
        /// </summary>
        public int[][] expectedTokenSequences;

        /// <summary> This is a reference to the "tokenImage" array of the generated
        /// parser within which the parse error occurred.  This array is
        /// defined in the generated ...Constants interface.
        /// </summary>
        public String[] tokenImage;

        /// <summary> The end of line string for this machine.</summary>
        protected internal string eol = Environment.NewLine;

        /// <summary> Used to convert raw characters to their escaped version
        /// when these raw version cannot be used as part of an ASCII
        /// string literal.
        /// </summary>
        protected internal virtual string Add_escapes(string str)
        {
            StringBuilder retval = new StringBuilder();
            char ch;
            for (int i = 0; i < str.Length; i++)
            {
                switch (str[i])
                {

                    case (char)(0):
                        continue;

                    case '\b':
                        retval.Append("\\b");
                        continue;

                    case '\t':
                        retval.Append("\\t");
                        continue;

                    case '\n':
                        retval.Append("\\n");
                        continue;

                    case '\f':
                        retval.Append("\\f");
                        continue;

                    case '\r':
                        retval.Append("\\r");
                        continue;

                    case '\"':
                        retval.Append("\\\"");
                        continue;

                    case '\'':
                        retval.Append("\\\'");
                        continue;

                    case '\\':
                        retval.Append("\\\\");
                        continue;

                    default:
                        if ((ch = str[i]) < 0x20 || ch > 0x7e)
                        {
                            System.String s = "0000" + System.Convert.ToString(ch, 16);
                            retval.Append("\\u" + s.Substring(s.Length - 4, (s.Length) - (s.Length - 4)));
                        }
                        else
                        {
                            retval.Append(ch);
                        }
                        continue;

                }
            }
            return retval.ToString();
        }

    }
}
